package com.liu.yanan.livestream;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.widget.TextView;
import android.widget.Toast;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import static android.app.PendingIntent.getActivity;

public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener,
        ActivityCompat.OnRequestPermissionsResultCallback{


    private TextureView mPreviewView;
    private Handler mHandler;
    private HandlerThread mThreadHandler;
    private Size mPreviewSize;
    private CaptureRequest.Builder mPreviewBuilder;
    private ImageReader mImageReader;
    private CameraDevice mCameraDevice;
    public static Encoder encoder;
    public static DatagramSocket socket;
    private H264Handler h264Handler;
    private Live555Native live555Native;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        live555Native = new Live555Native();

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.textview);
        tv.setText(live555Native.hello());
        Log.d("JNICall", live555Native.hello());


        // init preview
        mPreviewView = (TextureView) findViewById(R.id.textureview);
        mPreviewView.setSurfaceTextureListener(this);


        mThreadHandler = new HandlerThread("CAMERA2");
        mThreadHandler.start();
        mHandler = new Handler(mThreadHandler.getLooper());

        h264Handler = new H264Handler();

        try {
            socket = new DatagramSocket();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(encoder != null) {
            encoder.releaseMediaCode();
            encoder = null;
        }
    }


    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        try{
            CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics("0");
            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            //mPreviewSize = map.getOutputSizes(SurfaceTexture.class)[0];
            mPreviewSize = new Size(1280, 720);

            if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)  != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.CAMERA}, 1);
                Toast.makeText(this, "cameropen", 10).show();
                return;
            }

            cameraManager.openCamera("0", mcameracallback, mHandler);

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        if (requestCode == 1) {
            if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Request Permission Failed", Toast.LENGTH_LONG).show();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }


    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        Log.d("SurfaceCallback", "sizechange");
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        Log.d("SurfaceCallback", "destroy");
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        Log.d("SurfaceCallback", "update");
    }

    private CameraDevice.StateCallback mcameracallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            try {
                Log.d("CameraCallback", "Camera opened");
                mCameraDevice = camera;
//                startPreview(camera);
                SurfaceTexture texture = mPreviewView.getSurfaceTexture();
                texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                Surface surface = new Surface(texture);

                try{
                    mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }

                if(encoder == null) {
                    encoder = new Encoder(mPreviewSize.getWidth(), mPreviewView.getHeight(), 2000 * 1000, 15, h264Handler);
                }

                /*
                  要想帧率高，有三条路可走：
                    1.降低采集分辨率。但这样会降低视频效果。
                    2.提高硬件速度，使用高性能的处理器，这是最快的方法。
                    3.基于现状，改进软件算法，提高代码执行效率。
                 */
                Log.d("CameraCallback", "height:" + mPreviewSize.getHeight() + ", width:" + mPreviewSize.getWidth());
                // 方法1 确实好使
                //mImageReader = ImageReader.newInstance(300, 400, ImageFormat.YUV_420_888,2);
                mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888,2);
                mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler);
                mPreviewBuilder.addTarget(surface);
                mPreviewBuilder.addTarget(mImageReader.getSurface());
                mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionStateCallback, mHandler);

            }catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            Log.d("CamearaCallback", "Camera Disconected");
            camera.close();
            camera = null;
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            Log.d("CamearaCallback", "Camera Error");
            camera.close();
            camera = null;
        }
    };

    private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
            try {
                updatePreview(session);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {

        }
    };

    private void updatePreview(CameraCaptureSession session) throws CameraAccessException {
        session.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
    }


    private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {

            /* TODO:
                 把每一帧的处理放在另一个线程后，帧率会增加很多，看文档好像可以多个requestsession，可以达到full framerate
            */
            mHandler.post(new ImageConverter(reader.acquireNextImage()));
        }
    };

    private String getData() {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return format.format(date);
    }


    private static class ImageConverter implements Runnable {

        private final Image mImage;

        ImageConverter(Image image) {
            mImage = image;
        }

        @Override
        public void run() {

            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] data = new byte[buffer.remaining()];
            buffer.get(data);
            mImage.close();

            MainActivity.encoder.encodeYUV420(data);
            Log.d("GotYUVData", "YUV length: "+ String.valueOf(data.length));
        }
    }

    class H264Handler implements Encoder.IEncoderListener {
        @Override
        public void onH264(byte[] data) {
            try {
                InetAddress address = InetAddress.getByName("172.17.91.172");

                DatagramPacket packet = new DatagramPacket(data, 0, data.length, address, 5550);
                MainActivity.socket.send(packet);
                Log.d("GotH264Data", "H264 length: " + String.valueOf(data.length));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
